/*
 * Copyright (c) 2009, Katholieke Universiteit Leuven
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *     * Neither the name of the Katholieke Universiteit Leuven nor the names of
 *       its contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @addtogroup peers
 * @{
 */
/**
 * @file
 * The implementation of the LooCI peers library
 * @author
 * Wouter Horré <wouter.horre@cs.kuleuven.be>
 */

#include <string.h>
#include "peers.h"
#include "lib/vector.h"

#ifdef LOOCI_PEERS_DEBUG
#include <stdio.h>
#ifdef CONTIKI_TARGET_AVR_RAVEN
#include <avr/pgmspace.h>
#define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#else
#define PRINTF printf
#endif // CONTIKI_TARGET_AVR_RAVEN
#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#endif

/**
 * @internal
 * Structure for storing a peer
 */
struct peer {
  peer_id_t id;
  peer_addr_t addr;
};

/**
 * @internal
 * The vector to store the peers
 */
VECTOR(peers, struct peer);

/**
 * @internal
 * The next free id to use
 */
static peer_id_t next_id = 2; // we start at 2 (1 = PEER_ID_ALL)

/**
 * @internal
 * Grab an unused id
 *
 * @TODO Fix to prevent wrapping around, etc ...
 */
#define GRAB_ID next_id++

/**
 * @internal
 * Release an id
 *
 * @TODO implement
 */
#define RELEASE_ID(id)

void peer_init() {
  vector_libinit();
  vector_init(&peers);

  // add broadcast with id PEER_ID_ALL
  struct peer new_peer;
  new_peer.id = PEER_ID_ALL;
  memset(&(new_peer.addr), 0, sizeof(peer_addr_t));
  ((u8_t*)&(new_peer.addr))[0] = 0xFF;
  ((u8_t*)&(new_peer.addr))[1] = 0x02;
  ((u8_t*)&(new_peer.addr))[15] = 0x01;
  vector_add(&peers, &new_peer);
}

peer_id_t peer_add(peer_addr_t * addr) {
  struct peer new_peer;
  new_peer.id = GRAB_ID;
  memcpy(&(new_peer.addr), addr, sizeof(peer_addr_t));
  if(vector_add(&peers, &new_peer) == VECTOR_OK) {
    PRINTF("[LooCI Peers] Added new peer with id %u and address: ", new_peer.id);
    PRINT6ADDR(&new_peer.addr);
    PRINTF("\r\n");
    return new_peer.id;
  } else {
    return PEER_ID_NONE;
  }
}

void peer_remove(peer_id_t id) {
  int i;
  for(i=0; i < vector_len(&peers); ++i) {
    struct peer * thepeer = (struct peer *)vector_get(&peers, i);
    if(thepeer->id == id) {
      vector_remove(&peers, i);
      RELEASE_ID(id);
      break;
    }
  }
}

peer_addr_t * peer_get_addr(peer_id_t id) {
  int i;
  for(i=0; i < vector_len(&peers); ++i) {
    struct peer * thepeer = (struct peer *) vector_get(&peers, i);
    if(thepeer->id == id) {
      return &(thepeer->addr);
    }
  }
  return NULL;
}

peer_id_t peer_get_id(peer_addr_t * addr) {
  int i;
  for(i=0; i < vector_len(&peers); ++i) {
    struct peer * thepeer = (struct peer *) vector_get(&peers, i);
    if(memcmp(addr, &(thepeer->addr), sizeof(peer_addr_t))==0) {
      return thepeer->id;
    }
  }
  return PEER_ID_NONE;
}

peer_id_t peer_get_id_or_add(peer_addr_t * addr) {
  peer_id_t result = peer_get_id(addr);
  if(result == PEER_ID_NONE) {
    result = peer_add(addr);
  }
  return result;
}

/** @} */
